﻿/**************************************************************************
* @ file    : misc.h
* @ author  : yang yang
* @ version : 0.9
* @ date    : 2017.05.15
* @ brief   : 常用工具函数
* @Copyright (c) 2016  chuangmi inc.
***************************************************************************/
 
#ifndef __MISC_H__
#define __MISC_H__

// 行处理长度
#define LINE_BUFFER 1024

#ifdef __cplusplus
extern "C" {
#endif



    //----------------------------------------------------------------------------
    // 取随机数
    //----------------------------------------------------------------------------
    int imi_rand_range(int min, int max);

    //----------------------------------------------------------------------------
    // 随机休眠一段时间（单位ms)
    //----------------------------------------------------------------------------
    void imi_random_sleep(int minSleepMs, int maxSleepMs);

    /**
     * 执行一个系统命令
     *
     * @param cmd       要运行的系统命令
     * @param out_buf   [out] 用于保存系统输出的缓冲区
     * @param buf_size  缓冲区的最大大小
     * @return          命令执行后，读出的输出长度
     * @return          小于0：错误编码
     */
    int exec(const char *cmd, char *out_buf, int buf_size);

    // 创建共享内存
    typedef struct imi_sharemem_s {
        char* ptr;
        int size;
        
        int fd;         // 共享文件fd
        
        char* file;
        void* handle;   // 用于存储win32下共享文件的handle
    } *imi_sharemem_t;

    // 创建共享内存, 如已存在，则open。 用于服务端
    imi_sharemem_t imi_sharemem_create(const char *sharefile, size_t size);

    // 释放共享内存, 如已存在，则open。 用于服务端
    void imi_sharemem_destroy(imi_sharemem_t sharemem);

    // 打开共享内存，用于客户端
    imi_sharemem_t imi_sharemem_open(const char* sharefile, size_t size);

    // 关闭共享内存，用于客户端
    void imi_sharemem_close(imi_sharemem_t sharemem);

    // 从arguments读取对应key的value. 如果指定key不存在，则使用default value
    // 例如参数列表为 -c abc时:  调用get_args_string(argc, argv, "-c", NULL)获得abc
    // @argc            main函数输入的参数数组长度
    // @argv            main函数输入的参数数组
    // @key             需要读取的key值
    // @defaultValue    指定key值不存在或无效时，需要返回的default value
    // @return          key所对应的value值，或者default value. (注意，返回值为直接引用，并不分配内存)
    const char* get_args_string(int argc, char* const argv[], const char* key, const char* defaultValue);
    int get_args_int(int argc, char* const argv[], const char* key, const int defaultValue);
    long get_args_long(int argc, char* const argv[], const char* key, const long defaultValue);
    double get_args_double(int argc, char* const argv[], const char* key, const double defaultValue);

    // 检测arguments列表中是否存在指定的key
    int is_args_exist(int argc, char* const argv[], const char* key);

    // 计算比cap大的最小2的整数次方的整数
    // 例如： cap=17，21，31，32时，返回32
    int calc_table_size(int cap);

    // 读取当前线程号
    unsigned long imi_get_current_thread_id();

    // 针对int32的字典函数组，包括hash函数,key复制，销毁函数
    // 此时Key为整型指针.利用key指向的内容作为hash
    unsigned int32_hash(const void *key);
    int int32_comparer(const void *elem1, const void *elem2);
    void* int32_duplicate(const void* key);
    void int32_release(void* key);


    //----------------------------------------------------------------------------
    // 控制台操作封装
    // 
    //----------------------------------------------------------------------------   

    typedef void (*func_cmd_handler)(char* line, void* arg);
    typedef struct stdin_cmd_opt_s {
        char* cmd;
        func_cmd_handler handler;
    } *stdin_cmd_opt_t;


    // 等待console输入并处理指令. 缺省在console输入"exit"退出
    // @count:   预定义指令数组长度. 长度设为0则可以暂停console直到输入exit为止
    // @opts:    预定义的指令数组，  例如 {{'showlog', do_showlog}... }
    // @args:      启动handler时，传递给handler的参数
    void handle_stdin(int count, stdin_cmd_opt_t opts, void* args);


    // 从文件中读取所有二进制内容并返回缓冲区，调用者需要自己释放缓冲区
    // @file_size [out] 输出的缓冲区长度（文件长度）
    char *read_all_file_new(const char *filename, int *file_size);

    // 读取指定文件大小
    int get_file_size(const char *filename);

    //----------------------------------------------------------------------------
    // 进程读写锁封装: 基于fnctl实现.
    // 需要注意的是：同一个进程内不能实现线程之间互斥，但可以支持同进程内锁的重入。
    //----------------------------------------------------------------------------   
    
    // @return 大于等于0: 创建的fd. 小于0, 错误代码
    int imi_process_rwlock_create(const char* share_name);
    int imi_process_rwlock_close(int fd);
    
    // @fd: 创建时返回的描述符. 如果已有fd, 可以跳过create/close直接使用
    // @return 0: 成功, -1: 失败
    int imi_process_rwlock_rdlock(int fd);
    int imi_process_rwlock_wrlock(int fd);
    int imi_process_rwlock_tryrdlock(int fd);
    int imi_process_rwlock_trywrlock(int fd);
    int imi_process_rwlock_unlock(int fd);
    
    /***************************************************************************** 
     * 任务进度计算和回调工具函数集：
     * 在执行某个任务时，可能需要经过很多步骤。为了能够方便的在分步骤当中准确的上
     * 报整体任务的总体进度。可以使用该工具集。可以适用于ota升级等场景
     * 
     * 在某个子任务需要上报总体进度时, 子任务的调用者在调用之前需要设定本次子任务
     * 起始时占自身进度的百分比和子任务结束占自身任务的百分比，然后将上下文传给子
     * 任务。子任务在上报进度时，不需要关心整体进度如何计算，只需要调用report接口
     * 上报自身完成的百分比即可。接口会自动的计算出当前总任务的完成百分比，并通过
     * 回调函数上报进度。
     * 
     * 为了同一个上下文环境中支持多级子任务的进度计算。调用者在调用下级子任务之前
     * 需要首先保存当前计算上下文, 设置子任务进度计算时统一使用该上下文。例如下面
     * 这个例子：
     * 1) 总任务分成2个一级子任务，每个子任务占各占总任务的50%。 
     * 2) 每个一级子任务又可以分为4个二级子任务，每个二级子任务进度各占一级任务的
     *    25%
     * 
     * 则在使用progress对象上报总体任务进度时调用流程如下：
     *
     * 总任务: 创建progress
     *   一级子任务1: set_ctx(0% - 50%)
     *      二级子任务1_1: set_ctx(0% - 25%)
     *         执行过程中report(0 - 100%)
     *      二级子任务1_2: set_ctx(25% - 50%)
     *         执行过程中report(0 - 100%)
     *      二级子任务1_3: set_ctx(50% - 75%)
     *         执行过程中report(0 - 100%)
     *      二级子任务1_4: set_ctx(75% - 100%)
     *         执行过程中report(0 - 100%)
     *   一级子任务2: set_ctx(0% - 50%)
     *      二级子任务2_1: set_ctx(0% - 25%)
     *         执行过程中report(0 - 100%)
     *      二级子任务2_2: set_ctx(25% - 50%)
     *         执行过程中report(0 - 100%)
     *      二级子任务2_3: set_ctx(50% - 75%)
     *         执行过程中report(0 - 100%)
     *      二级子任务2_4: set_ctx(75% - 100%)
     *         执行过程中report(0 - 100%)         
     * 
     * 在使用过程中，子任务的级别可以无限分下去。但在使用时，各级子任务都不需要关
     * 心如何换算总任务的进度，只需要根据自己的层级上报自身任务完成的百分比即可
     ****************************************************************************/
    
    // 进度回调函数定义
    typedef void(*func_imi_progress_cb)(float progress, void* userdata);
       
    // 进度计算对象
    typedef struct imi_progress_s *imi_progress_t;

    // 进度计算上下文
    typedef struct {
      float start_percent;
      float percent_len;
    } imi_progress_ctx_t;

    // 创建一个进度计算对象。由总任务的调用者创建并传递给多级子任务使用
    // @param cb            上报进度的回调函数
    // @param userdata      上报进度时，需要带回的userdata
    // @param report_step   进度上报间隔,取值(0-1). 例如每5%报一次总进度设为0.05
    // @return              进度上下文实例
    imi_progress_t imi_progress_create(func_imi_progress_cb cb, void *userdata,
                   float report_step);

    // 获取进度执行上下文. 调用者在设置所有子任务之前，需要首先获取该上下文
    // @param pobj          progress对象
    // @param ctx           [out] 获得当前progress对象的上下文
    void imi_progress_get_ctx(imi_progress_t pobj, imi_progress_ctx_t *ctx);

    // 设置某个子任务的进度计算参数. 在子任务执行前调用。然后再将progress对象
    // 传递给子任务. 子任务执行时直接调用report接口上报进度
    //
    // @param pobj          progress对象
    // @param ctx           执行子任务前，从get ctx时获取的进度上下文
    // @param start_percent 子任务的起始时占本任务的百分比，取值0-1
    // @param end_percent   子任务的结束时占本任务的百分比，取值0-1 
    void imi_progress_set_ctx(imi_progress_t pobj, imi_progress_ctx_t *ctx, 
                              float start_percent, float end_percent);
  
    // 上报任务进度. 
    // @param pobj          progress对象
    // @param percent       当前任务执行的百分比（0-1）    
    void imi_progress_report(imi_progress_t pobj, float percent); 
    
    // 释放进度对象
    void imi_progress_release(imi_progress_t pobj);
    
#ifdef __cplusplus
}
#endif

#endif // __MISC_H__